package com.tianxiabuyi.persist;


import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.*;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

/**
 * 数据源对象,一个DataSource代表一个数据源
 */
public class DataSource {
    public static HashMap<String, HashMap<String, Method>> getterMaps = new HashMap<String, HashMap<String, Method>>();
    public static HashMap<String, HashMap<String, Method>> setterMaps = new HashMap<String, HashMap<String, Method>>();
    private static final String multiDB = "multiDB.properties";
    // 数据源名称
    private String name;
    // 数据库配置文件文件名
    private String cfgPath;
    // hibernate映射配置文件名
    private String hbmPath;
    // 负责初始化hibernate
    private SessionFactory sf;
    // hibernage配置
    private Configuration cfg;
    // 所有模型字节码文件（所有数据源共享相同的字节码）
    private static Class<?>[] modelClasses;
    // 数据源名称和数据源对象
    private static HashMap<String, DataSource> dataSources = new HashMap<String, DataSource>();

    public SessionFactory getSf() {
        return sf;
    }

    // 初始化
    static {
        initModelClasses();
        initModelAccessors();
        // 读取多数据源配置文件
        initDataSource();
    }

    private DataSource() {
    }

    /**
     * 重新设置数据源(一般用于修改了数据库字段后，不重启服务器)
     */
    public static void resetDataSources() {
        // 关闭连接
        for (Map.Entry<String, DataSource> entry : dataSources.entrySet()) {
            if (entry.getValue().sf != null) {
                System.out.println(entry.getKey() + "数据源 关闭连接");
                entry.getValue().sf.close();
            }
        }

        // 清空dataSources
        dataSources.clear();

        initModelClasses();
        initModelAccessors();
        // 读取多数据源配置文件
        initDataSource( );
    }

    /**
     * 赋值模型字节码文件
     */
    public static void initModelClasses() {
        try {
            Properties p = multiDB();
            String names = p.get("classPackage")+""; // 数据源名称
            if ((!names.equals("")) && (!names.equals("null"))) {
                modelClasses = PersistTools.getClasses(names);
            } else {
                System.out.println("没有读取到数据模型");
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 模型中非私有方法放入容器中
     */
    public static void initModelAccessors() {
        getterMaps = new HashMap<String, HashMap<String, Method>>();
        setterMaps = new HashMap<String, HashMap<String, Method>>();
        if (modelClasses == null) {
            return;
        }
        Class<?>[] cs = modelClasses;
        for (int i = 0; i < cs.length; i++) {
            Method[] ms = cs[i].getMethods();
            System.out.println("init:" + cs[i].getName());
            HashMap<String, Method> getters = getterMaps.get(cs[i]
                    .getName());
            if (getters == null) {
                getters = new HashMap<String, Method>();
                getterMaps.put(cs[i].getName(), getters);
            }
            HashMap<String, Method> setters = setterMaps.get(cs[i]
                    .getName());
            if (setters == null) {
                setters = new HashMap<String, Method>();
                setterMaps.put(cs[i].getName(), setters);
            }
            for (int j = 0; j < ms.length; j++) {
                if (ms[j].getName().startsWith("get")) {
                    getters.put(ms[j].getName(), ms[j]);
                } else if (ms[j].getName().startsWith("set")) {
                    setters.put(ms[j].getName(), ms[j]);
                }
            }
        }
    }

    /**
     * 读取多数据源配置文件
     */
    private static void initDataSource() {
        // 加载多数据源配置文件
        Properties p = multiDB();

        String[] names = p.get("name").toString().split(","); // 数据源名称
        String[] cfgPaths = p.get("cfgPath").toString().split(","); // 数据库配置文件文件名
        String[] hbmPaths = p.get("hbmPath").toString().split(","); // hibernate映射配置文件名
        // 加载数据源配置和hibernate配置文件
        int count = names.length;
        for (int i = 0; i < count; i++) {
            if (i < cfgPaths.length && i < hbmPaths.length) {
                String name = names[i];
                String cfgPath = cfgPaths[i];
                String hbmPath = hbmPaths[i];

                // 数据源对象
                DataSource dataSource = dataSources.get(name);
                if (dataSource == null) {
                    dataSource = new DataSource();
                }
                dataSource.name = name;
                ;
                dataSource.hbmPath = hbmPath;
                dataSource.cfgPath = cfgPath;
                dataSources.put(name, dataSource);

                System.out.println("读取到一个数据源配置文件, 数据源名称:" + name
                        + ", hibernate映射文件:" + hbmPath + ", 数据库连接配置文件:"
                        + cfgPath);

                // 建立数据源连接
                getDataSource(name);
            }
        }
    }

    private static Properties multiDB() {
        Properties p = new Properties();
        InputStream is = DataSource.class.getClassLoader().getResourceAsStream(multiDB);
        try {
            p.load(is);
        } catch (Exception e) {
        }
        return p;
    }

    /**
     * 根据数据源名称去初始化Configuration和SessionFactory
     */
    @SuppressWarnings("deprecation")
    public static DataSource getDataSource(String dataSourceName) {
        if (null == dataSourceName)
            throw new NullPointerException("dataSourceName为空");

        DataSource dataSource = dataSources.get(dataSourceName);
        if (dataSource == null)
            throw new NullPointerException("数据源配置文件中没有该名称");

        if (dataSource.cfg != null) {
            return dataSource;
        }

        System.out.println(dataSourceName + "建立数据源连接");
        // Configuration对象
        File file = new File(DataSource.class.getClassLoader().getResource(dataSource.hbmPath).getFile());
        Configuration cfg = new Configuration();
        cfg.addFile(file); // hibernate映射文件

        // class字节码文件
        if (modelClasses != null) {
            for (int i = 0; i < modelClasses.length; i++) {
                cfg.addAnnotatedClass(modelClasses[i]);
            }
        }

        // hibernate配置文件
        ClassLoader classLoader = Thread.currentThread()
                .getContextClassLoader();
        Properties p = new Properties();
        InputStream is = DataSource.class.getClassLoader().getResourceAsStream(dataSource.cfgPath);
        try {
            p.load(is);
        } catch (Exception e) {
        }
        cfg.setProperties(p);
        dataSource.cfg = cfg;
        dataSource.sf = cfg.buildSessionFactory();
        dataSources.put(dataSource.name, dataSource);

        return dataSource;
    }
}